﻿using System.Collections.Generic;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.UI;

namespace BToolkit
{
    [RequireComponent(typeof(RawImage))]
    public class WritingBrush : MonoBehaviour
    {
        public bool canPaint = true;
        public bool useCanvasTexture = true;
        public bool soft;
        public Shader shader;
        public Texture penTexture;
        public Color brushColor = Color.black;
        public int num = 100;
        public float brushSize = 0.3f;
        public float spilling = 0f;
        public bool isEmpty { get; private set; }
        public RenderTexture renderTexture { get; set; }
        private Texture canvasTexture;
        private Material material;
        private RawImage rawImage;
        private float brushScale;
        private Vector2[] PositionArray1 = new Vector2[4];
        private int b = 0;
        private float[] speedArray = new float[4];
        private int s = 0;

        public RectTransform rectTransform { get { return transform as RectTransform; } }
        Vector2 rawZeroPos; //raw图片的左下角位置
        float rawWidth;  //raw图片宽度
        float rawHeight; //raw图片长度

        Vector2 startPosition = Vector2.zero;
        Vector2 endPosition = Vector2.zero;
        PointerEventData pointerEventData;
        GraphicRaycaster graphicRaycaster;
        List<RaycastResult> results;

        void Awake()
        {
            isEmpty = true;
            rawImage = GetComponent<RawImage>();
            canvasTexture = rawImage.texture;
            material = new Material(shader);
            material.SetTexture("_MainTex", penTexture);
            pointerEventData = new PointerEventData(EventSystem.current);
            graphicRaycaster = GameObject.FindObjectOfType<GraphicRaycaster>();
            results = new List<RaycastResult>();
        }

        void OnEnable()
        {
            startPosition = Vector2.zero;
            PositionArray1 = new Vector2[4];
            b = 0;
            speedArray = new float[4];
            s = 0;
        }

        void Start()
        {
            InitPos();
        }

        /// <summary>
        /// 画布移动后重新初始化基点位置
        /// </summary>
        public void InitPos()
        {
            if (!rawImage)
            {
                rawImage = GetComponent<RawImage>();
                canvasTexture = rawImage.texture;
                material = new Material(shader);
                material.SetTexture("_MainTex", penTexture);
            }

            rawZeroPos = rawImage.rectTransform.anchoredPosition - rawImage.rectTransform.sizeDelta * 0.5f;

            //raw图片鼠标位置，宽度计算
            rawWidth = rawImage.rectTransform.sizeDelta.x;
            rawHeight = rawImage.rectTransform.sizeDelta.y;
            rawZeroPos = rawImage.rectTransform.anchoredPosition - rawImage.rectTransform.sizeDelta * 0.5f;
            renderTexture = new RenderTexture((int)rawWidth, (int)rawHeight, 24, RenderTextureFormat.ARGB32);
            if (useCanvasTexture)
            {
                Clear(canvasTexture);
                TextureLoader textureLoader = GetComponent<TextureLoader>();
                if (textureLoader)
                {
                    if (textureLoader.texture) Clear(textureLoader.texture);
                    textureLoader.onLoadedAction = () =>
                    {
                        Clear(textureLoader.texture);
                    };
                }
            }
        }

        void Update()
        {
            if (canPaint)
            {
                if (Input.GetMouseButton(0))
                {
                    pointerEventData.position = Input.mousePosition;
                    results.Clear();
                    graphicRaycaster.Raycast(pointerEventData, results);
                    if (results.Count != 0 && results[0].gameObject == gameObject)
                    {
                        isEmpty = false;
                        OnMouseMove(BUtils.ScreenToUGUIPoint(Input.mousePosition));
                    }
                }
                if (Input.GetMouseButtonUp(0))
                {
                    OnMouseUp();
                }
                DrawImage();
            }
        }

        void OnMouseUp()
        {
            startPosition = Vector2.zero;
            b = 0;
            s = 0;
        }

        //设置画笔宽度
        float SetScale(float distance)
        {
            float Scale = 0;
            if (distance < 100)
            {
                Scale = 0.8f - 0.005f * distance;
            }
            else
            {
                Scale = 0.425f - 0.00125f * distance;
            }
            if (Scale <= 0.05f)
            {
                Scale = 0.05f;
            }
            return Scale * brushSize;
        }

        void OnMouseMove(Vector2 pos)
        {
            if (startPosition == Vector2.zero)
            {
                startPosition = pos;
            }
            endPosition = pos;
            float distance = Vector2.Distance(startPosition, endPosition);
            brushScale = SetScale(distance);
            ThreeOrderBezierCurse(pos, distance, spilling);

            startPosition = endPosition;
        }

        public void Clear(Texture canvasTexture = null)
        {
            if (canvasTexture)
            {
                this.canvasTexture = canvasTexture;
            }
            Graphics.SetRenderTarget(renderTexture);
            GL.PushMatrix();
            GL.Clear(true, true, Color.white);
            GL.PopMatrix();
            if (useCanvasTexture)
            {
                Graphics.Blit(this.canvasTexture, renderTexture);
            }
            startPosition = Vector2.zero;
            PositionArray1 = new Vector2[4];
            b = 0;
            speedArray = new float[4];
            s = 0;
            isEmpty = true;
        }

        void DrawBrush(RenderTexture destTexture, float x, float y, Texture sourceTexture, Color color, float scale)
        {
            //增加鼠标位置根据raw图片位置换算。
            float rectX = x - rawZeroPos.x;
            float rectY = y - rawZeroPos.y;
            Rect rect = new Rect(rectX, rectY, sourceTexture.width * scale, sourceTexture.height * scale);
            //笔刷相对于鼠标剧中
            rect.x -= rect.width * 0.5f;
            rect.y += rect.height * 0.5f;
            float left = rect.x;
            float right = rect.x + rect.width;
            float top = rect.y;
            float bottom = rect.y - rect.height;

            Graphics.SetRenderTarget(destTexture);

            GL.PushMatrix();
            GL.LoadOrtho();

            material.SetTexture("_MainTex", penTexture);
            material.SetColor("_Color", color);
            material.SetPass(0);

            GL.Begin(GL.QUADS);

            GL.TexCoord2(0.0f, 1.0f); GL.Vertex3(left / rawWidth, top / rawHeight, 0);
            GL.TexCoord2(1.0f, 1.0f); GL.Vertex3(right / rawWidth, top / rawHeight, 0);
            GL.TexCoord2(1.0f, 0.0f); GL.Vertex3(right / rawWidth, bottom / rawHeight, 0);
            GL.TexCoord2(0.0f, 0.0f); GL.Vertex3(left / rawWidth, bottom / rawHeight, 0);

            GL.End();
            GL.PopMatrix();
        }

        void DrawImage()
        {
            rawImage.texture = renderTexture;
        }

        //三阶贝塞尔曲线，获取连续4个点坐标，通过调整中间2点坐标，画出部分（使用了num/1.5实现画出部分曲线）来使曲线平滑;通过速度控制曲线宽度。
        private void ThreeOrderBezierCurse(Vector2 pos, float distance, float targetPosOffset)
        {
            //记录坐标
            PositionArray1[b] = pos;
            b++;
            //记录速度
            speedArray[s] = distance;
            s++;
            if (b == 4)
            {
                Vector2 temp1 = PositionArray1[1];
                Vector2 temp2 = PositionArray1[2];

                //修改中间两点坐标
                Vector2 middle = (PositionArray1[0] + PositionArray1[2]) / 2;
                PositionArray1[1] = (PositionArray1[1] - middle) * 1.5f + middle;
                middle = (temp1 + PositionArray1[3]) / 2;
                PositionArray1[2] = (PositionArray1[2] - middle) * 2.1f + middle;

                for (int index1 = 0; index1 < num / 1.5f; index1++)
                {
                    float t1 = (1.0f / num) * index1;
                    Vector2 target = Mathf.Pow(1 - t1, 3) * PositionArray1[0] +
                                     3 * PositionArray1[1] * t1 * Mathf.Pow(1 - t1, 2) +
                                     3 * PositionArray1[2] * t1 * t1 * (1 - t1) + PositionArray1[3] * Mathf.Pow(t1, 3);
                    float deltaspeed = (float)(speedArray[3] - speedArray[0]) / num;
                    float randomOffset = 0;
                    if (soft)
                    {
                        randomOffset = Random.Range(-targetPosOffset, targetPosOffset);
                    }
                    DrawBrush(renderTexture, target.x + randomOffset, target.y + randomOffset, penTexture, brushColor, SetScale(speedArray[0] + (deltaspeed * index1)));
                }

                PositionArray1[0] = temp1;
                PositionArray1[1] = temp2;
                PositionArray1[2] = PositionArray1[3];

                speedArray[0] = speedArray[1];
                speedArray[1] = speedArray[2];
                speedArray[2] = speedArray[3];
                b = 3;
                s = 3;
            }
            else
            {
                DrawBrush(renderTexture, endPosition.x, endPosition.y, penTexture, brushColor, brushScale);
            }
        }
    }
}